package com.artnology.independence.views.jung;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.Iterator;

import javax.swing.JComponent;

import com.artnology.independence.Activator;
import com.artnology.independence.views.DependencyViewer;

import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.VisualizationServer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.AbstractGraphMousePlugin;
import edu.uci.ics.jung.visualization.picking.PickedState;

public class PickingGraphMousePlugin<V, E> extends AbstractGraphMousePlugin
		implements MouseListener, MouseMotionListener {
	protected V vertex;
	protected E edge;
	protected double offsetx;
	protected double offsety;
	protected boolean locked;
	protected int addToSelectionModifiers;
	protected Rectangle2D rect = new Rectangle2D.Float();
	protected VisualizationServer.Paintable lensPaintable;
	protected Color lensColor = Color.cyan;
	private DependencyViewer myViewer = null;

	public PickingGraphMousePlugin(DependencyViewer myViewer) {
		this(16, 17);
		
		this.myViewer = myViewer;
	}

	public PickingGraphMousePlugin(int selectionModifiers,
			int addToSelectionModifiers) {
		super(selectionModifiers);
		this.addToSelectionModifiers = addToSelectionModifiers;
		this.lensPaintable = new LensPaintable();
		this.cursor = Cursor.getPredefinedCursor(12);
	}

	public Color getLensColor() {
		return this.lensColor;
	}

	public void setLensColor(Color lensColor) {
		this.lensColor = lensColor;
	}

	public void mousePressed(MouseEvent e) {
		this.down = e.getPoint();
		VisualizationViewer vv = (VisualizationViewer) e.getSource();
		GraphElementAccessor pickSupport = vv.getPickSupport();
		PickedState pickedVertexState = vv.getPickedVertexState();
		PickedState pickedEdgeState = vv.getPickedEdgeState();
		if ((pickSupport != null) && (pickedVertexState != null)) {
			Layout layout = vv.getGraphLayout();
			if (e.getModifiers() == this.modifiers) {
				this.rect.setFrameFromDiagonal(this.down, this.down);

				Point2D ip = e.getPoint();

				this.vertex = (V) pickSupport.getVertex(layout, ip.getX(),
						ip.getY());
				if (this.vertex != null) {
					if (!pickedVertexState.isPicked(this.vertex)) {
						pickedVertexState.clear();
						pickedVertexState.pick(this.vertex, true);
					}

					Point2D q = (Point2D) layout.transform(this.vertex);

					Point2D gp = vv.getRenderContext()
							.getMultiLayerTransformer()
							.inverseTransform(Layer.LAYOUT, ip);

					this.offsetx = (float) (gp.getX() - q.getX());
					this.offsety = (float) (gp.getY() - q.getY());
				} else if ((this.edge = (E) pickSupport.getEdge(layout,
						ip.getX(), ip.getY())) != null) {
					pickedEdgeState.clear();
					pickedEdgeState.pick(this.edge, true);
				} else {
					vv.addPostRenderPaintable(this.lensPaintable);
					pickedEdgeState.clear();
					pickedVertexState.clear();
				}
			} else if (e.getModifiers() == this.addToSelectionModifiers) {
				vv.addPostRenderPaintable(this.lensPaintable);
				this.rect.setFrameFromDiagonal(this.down, this.down);
				Point2D ip = e.getPoint();
				this.vertex = (V) pickSupport.getVertex(layout, ip.getX(),
						ip.getY());
				if (this.vertex != null) {
					boolean wasThere = pickedVertexState.pick(this.vertex,
							!pickedVertexState.isPicked(this.vertex));
					if (wasThere) {
						this.vertex = null;
					} else {
						Point2D q = (Point2D) layout.transform(this.vertex);

						Point2D gp = vv.getRenderContext()
								.getMultiLayerTransformer()
								.inverseTransform(Layer.LAYOUT, ip);

						this.offsetx = (float) (gp.getX() - q.getX());
						this.offsety = (float) (gp.getY() - q.getY());
					}
				} else if ((this.edge = (E) pickSupport.getEdge(layout,
						ip.getX(), ip.getY())) != null) {
					pickedEdgeState.pick(this.edge,
							!pickedEdgeState.isPicked(this.edge));
				}
			}
		}
		if (this.vertex != null)
			e.consume();
	}

	public void mouseReleased(MouseEvent e) {

		VisualizationViewer vv = (VisualizationViewer) e.getSource();
		if (e.getModifiers() == this.modifiers) {
			if (this.down != null) {
				Point2D out = e.getPoint();

				if ((this.vertex == null)
						&& (!heyThatsTooClose(this.down, out, 5.0D)))
					pickContainedVertices(vv, this.down, out, true);
			}
		} else if ((e.getModifiers() == this.addToSelectionModifiers)
				&& (this.down != null)) {
			Point2D out = e.getPoint();

			if ((this.vertex == null)
					&& (!heyThatsTooClose(this.down, out, 5.0D))) {
				pickContainedVertices(vv, this.down, out, false);
			}
		}

		this.down = null;
		this.vertex = null;
		this.edge = null;
		this.rect.setFrame(0.0D, 0.0D, 0.0D, 0.0D);
		vv.removePostRenderPaintable(this.lensPaintable);
		if (myViewer.getPickedVertexState()
				.getPicked().size() > 1) {
			Activator.getDefault().getModel().getMyGraph().refreshGraph(myViewer.getPickedVertexState().getPicked());
		
		}
	}

	public void mouseDragged(MouseEvent e) {
		if (!this.locked) {
			VisualizationViewer vv = (VisualizationViewer) e.getSource();
			if (this.vertex != null) {
				Point p = e.getPoint();
				Point2D graphPoint = vv.getRenderContext()
						.getMultiLayerTransformer().inverseTransform(p);
				Point2D graphDown = vv.getRenderContext()
						.getMultiLayerTransformer().inverseTransform(this.down);
				Layout layout = vv.getGraphLayout();
				double dx = graphPoint.getX() - graphDown.getX();
				double dy = graphPoint.getY() - graphDown.getY();
				PickedState ps = vv.getPickedVertexState();

				for (Iterator i$ = ps.getPicked().iterator(); i$.hasNext();) {
					Object v = i$.next();
					Point2D vp = (Point2D) layout.transform(v);
					vp.setLocation(vp.getX() + dx, vp.getY() + dy);
					layout.setLocation(v, vp);
				}
				this.down = p;
			} else {
				Point2D out = e.getPoint();
				if ((e.getModifiers() == this.addToSelectionModifiers)
						|| (e.getModifiers() == this.modifiers)) {
					this.rect.setFrameFromDiagonal(this.down, out);
				}
			}
			if (this.vertex != null)
				e.consume();
			vv.repaint();
		}
	}

	private boolean heyThatsTooClose(Point2D p, Point2D q, double min) {
		return (Math.abs(p.getX() - q.getX()) < min)
				&& (Math.abs(p.getY() - q.getY()) < min);
	}

	protected void pickContainedVertices(VisualizationViewer<V, E> vv,
			Point2D down, Point2D out, boolean clear) {
		Layout layout = vv.getGraphLayout();
		PickedState pickedVertexState = vv.getPickedVertexState();

		Rectangle2D pickRectangle = new Rectangle2D.Double();
		pickRectangle.setFrameFromDiagonal(down, out);
		Iterator i$;
		if (pickedVertexState != null) {
			if (clear) {
				pickedVertexState.clear();
			}
			GraphElementAccessor pickSupport = vv.getPickSupport();

			Collection picked = pickSupport.getVertices(layout, pickRectangle);
			for (i$ = picked.iterator(); i$.hasNext();) {
				Object v = i$.next();
				pickedVertexState.pick(v, true);
			}
		}
	}

	public void mouseClicked(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
		JComponent c = (JComponent) e.getSource();
		c.setCursor(this.cursor);
	}

	public void mouseExited(MouseEvent e) {
		JComponent c = (JComponent) e.getSource();
		c.setCursor(Cursor.getPredefinedCursor(0));
	}

	public void mouseMoved(MouseEvent e) {
	}

	public boolean isLocked() {
		return this.locked;
	}

	public void setLocked(boolean locked) {
		this.locked = locked;
	}

	class LensPaintable implements VisualizationServer.Paintable {
		LensPaintable() {
		}

		public void paint(Graphics g) {
			Color oldColor = g.getColor();
			g.setColor(PickingGraphMousePlugin.this.lensColor);
			((Graphics2D) g).draw(PickingGraphMousePlugin.this.rect);
			g.setColor(oldColor);
		}

		public boolean useTransform() {
			return false;
		}
	}
}